-- Socket
local skynet = require "skynet";
local driver = require "skynet.socketdriver";
local netpack = require "skynet.netpack";
local log = require "log";

-- 消息队列
local queue

-- socket api
local socket = {}

-- socket id
local socketid = nil;

-- socket event
local socketevent = setmetatable(
    {}, 
    {
        __gc = function ()
            netpack.clear(queue);
        end
    }
)


-- 处理函数
local handler = nil;

-- 注册处理函数
function socket.register(inhandler)
    assert(inhandler.on_message, "inhandler must have 'on_message' function");
    assert(inhandler.on_connect, "inhandler must have 'on_connect' function");
    handler = inhandler;
end

-- 注销处理函数
function socket.unregister()
    handler = nil;
end

-- 连接至目标(待实现)
function socket.connect(addr, port)
    
end

-- 监听端口
function socket.listen(host, port, backlog)
    assert(handler, "use socket.register first")
    if nil == port then
        host, port = string.match(host, "([^:]+):(.+)$");
        port = tonumber(port);
    end
    socketid = driver.listen(host, port, backlog);
    return socketid;
end

-- 开启接收
function socket.start(fd)
    driver.start(fd);
end

-- 关闭socket
function socket.close(fd)
    driver.close(fd);
end

socket.send = assert(driver.send)
socket.lsend = assert(driver.lsend)
socket.header = assert(driver.header)
socket.nodelay = assert(driver.nodelay)

-- socket连接事件
function socket.on_connect(fd, msg)
    if nil == handler then
        return;
    end
    handler.on_connect(fd, msg)
end

-- socket消息事件
function socket.on_message(fd, msg, sz)
    if nil == handler then
        -- 没有处理函数，但是得把msg释放了
        skynet.error(string.format("Drop message from fd (%d) : %s", fd, netpack.tostring(msg,sz)))
        return;
    end
    -- handler.on_message(fd, netpack.tostring(msg, sz), sz);
    -- 这里不在处理释放msg转换为字符串的逻辑，因为转发时的问题，所以这里释放不释放没有意义
    handler.on_message(fd, msg, sz);
end

-- socket关闭事件
function socket.on_close(fd)
    if fd == socketid then
        socketid = nil;
        return;
    end

    if nil == handler or nil == handler.on_disconnect then
        return;
    end
    handler.on_disconnect(fd)
end

-- socket错误事件
function socket.on_error(fd, msg)
    if fd == socketid then
        socket.close(fd);
        log.info("close listen socket, accpet error: %s!", msg);
    else
        if nil == handler or nil == handler.on_error then
            return;
        end
        handler.on_error(fd, msg);
    end
end

-- socket警告事件
function socket.on_warning(fd, sz)
    if nil == handler or nil == handler.on_warning then
        return;
    end
    handler.on_warning(fd, sz);
end

-- socket event --
-- 连接事件
function socketevent.open(fd, msg)
    socket.on_connect(fd, msg);
end

-- 消息事件
function socketevent.data(fd, msg, sz)
    socket.on_message(fd, msg, sz)
end

-- 分包事件
function socketevent.more()
    local fd, msg, sz = netpack.pop(queue)
    if fd then
        skynet.fork(socketevent.more);
        socketevent.data(fd, msg, sz);
        for fd, msg, sz in netpack.pop, queue do
            socketevent.data(fd, msg, sz);
        end
    end
end

-- 关闭事件
function socketevent.close(fd)
    socket.on_close(fd)
end

-- 错误事件
function socketevent.error(fd, msg)
    socket.on_error(fd, msg)
end

-- 警告事件
function socketevent.warning(fd, size)
    socket.on_warning(fd, size);
end

skynet.register_protocol {
    name = "socket",
    id = skynet.PTYPE_SOCKET,   -- PTYPE_SOCKET = 6
    unpack = function (msg, sz)
        return netpack.filter(queue, msg, sz)
    end,
    dispatch = function (_, _, q, type, ...)
        queue = q
        if type then
            socketevent[type](...)
        end
    end
}

return socket

